import sys
import glob
import serial
import struct
import time
import numpy as np
from cion.hardware.emccd import *

#bytes_number = 3 #how many bytes represent a number
#channel_returned = 4 #how many channel returned, and should be converted to states

bytes_number = 2
channel_returned = 32
multi_channel_PMT = serial.Serial(
    port="COM6",
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_TWO,
    bytesize=serial.EIGHTBITS)

def serial_ports():
    """ Lists serial port names

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
    """
    if sys.platform.startswith('win'):
        ports = ['COM%s' % (i + 1) for i in range(256)]
    elif sys.platform.startswith('linux ') or sys.platform.startswith('cygwin'):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')

    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result

def seq_send(ser, packets, threshold):
    return_data_length = int(packets[1]/12*64)
    ser.write(bytes.fromhex(packets[0]))   #send sequence to FPGA
    resultsFPGA = ser.read(return_data_length)  #read the returned data from FPGA
    #resultsFPGA = multi_channel_PMT.read(int(packets[1])) #read the returned data from Multichannel PMT
    resultsFPGA = [int.from_bytes(resultsFPGA[i:i+bytes_number],"big") for i in range(0,return_data_length,bytes_number)]
    if threshold[0] != None and threshold[1]:
        #assert len(threshold[0])==channel_returned, "Threshold should matches with the number of channel being returned."
        resultsFPGA = np.array([np.array(resultsFPGA[i+channel_returned-1]) >= np.array(threshold[0]) for i in range(0,len(resultsFPGA),channel_returned)]).flatten().astype(int)
    #return resultsFPGA[16::32]
    return resultsFPGA
    


def seq_send_debug(ser, packets):
    ser.write(bytes.fromhex(packets[0]))   #send sequence to FPGA
    resultsFPGA = ser.read()  #read all returned data from FPGA
    resultsFPGA = list(resultsFPGA)
    return resultsFPGA

def seq_send_camera(ser, packets):
    ser.write(bytes.fromhex(packets[0]))   #send sequence to FPGA
    resultsFPGA = ser.read(packets[1])  #read the returned data from FPGA
    resultsFPGA = list(resultsFPGA)
    return resultsFPGA


def pulse_durations(sequence_dict):
    seq_string = sequence_dict['sequence'].replace('\t','')
    seq_string = seq_string.split('\n')
    seq_new = []
    for i in range(len(seq_string)):
        if seq_string[i]!='':
            seq_new = seq_new + [seq_string[i]]

    duration_list = []
    for element in seq_new:
        duration_list = duration_list +[element.split(' ')]
    return duration_list

def timestamp(duration, clock_period):
    # convert duration to a 40 bit binary string
    N = int(duration/clock_period-1)
    return timestamp_generator(N)

def timestamp_generator(N):
    W = 40 
    # convert number N to a W-bit binary timestap
    T = (W-N)&(2**W-1)
    T = '{0:040b}'.format(T)
    Res = ''
    for i in range(W):
        j = (int(T[W-1-i:W],2) < i)|(int(T[W-1-i:W],2) >= (1<<i) + i)
        Res = str(int(j)) + Res
    return Res

def chapter_padding(chapter):
    return chapter[:18]+'{0:08b}'.format(0) + ' '+chapter[18:]

def chapter_durations(pulses, dict):
    #convert [['chapter name',duration],...] array to [[00000,100],...] array
    duration_list = [] 
    for element in pulses:
        duration_list = duration_list + [[dict[element[0]],float(element[1])]]
    return duration_list

def pre_binary(pulses, clock_period):
    # change [chapter, duration] to binary strings
    duration_list = [] 
    for element in pulses:
        duration_list = duration_list + [[chapter_padding(element[0]),timestamp(element[1], clock_period)]]
    return duration_list

def packets_generator(pulses, ext_trig, repeat):
    start_packet = '100' + str(int(ext_trig)) + '{0:052b}'.format(0) + '{0:024b}'.format(int(repeat))
    pulse_packates = [start_packet]
    i = 0
    time_packets_length = len(pulses)+3
    # convert pulses to timestamp
    for element in pulses:
        packet = '010'
        packet = packet + '{0:05b}'.format(0) + element[1] + element[0].replace(' ','')
        pulse_packates = pulse_packates + [packet]
        i += 1
    # padding the time packet
    if time_packets_length < 15:
        for i in range(15 - time_packets_length):
            packet = '010'
            packet = packet + '{0:05b}'.format(0) + timestamp_generator(1-1) + '{0:032b}'.format(0)
            pulse_packates = pulse_packates + [packet]

    if time_packets_length%5 != 0&time_packets_length >= 15:
        for i in range(5 - time_packets_length%5):
            packet = '010'
            packet = packet + '{0:05b}'.format(0) + timestamp_generator(1-1) + '{0:032b}'.format(0)
            pulse_packates = pulse_packates + [packet]
    # generate end packets
    end_packets_3 = '010' + '{0:05b}'.format(0) + timestamp_generator(8-1) + '{0:032b}'.format(0)
    end_packets_2 = '010' + '{0:05b}'.format(0) + timestamp_generator(2-1) + '{0:032b}'.format(0)
    end_packets_1 = '011' + '{0:05b}'.format(0) + timestamp_generator(1-1) + '{0:032b}'.format(0)
    pulse_packates = pulse_packates + [end_packets_3, end_packets_2, end_packets_1]
    pulse_packates_hex = ''
    for packet in pulse_packates:
        pulse_packates_hex = pulse_packates_hex + hex(int(packet,2))[2:] + ' '
    # end_packet = 
    return pulse_packates_hex